package com.liush.system.aspect;

import com.alibaba.fastjson.JSON;
import com.liush.common.helper.JwtHelper;
import com.liush.model.controller.SysOperLog;
import com.liush.system.annotation.Log;
import com.liush.system.service.OperLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

/*
 * 日志的切面类
 * */
@Aspect
@Component
public class LogAspect {

    @Autowired
    OperLogService operLogService;

    //各种通知(日志中需要用 返回通知,异常通知)
    /*
     * joinPoint  连接点对象  (获取目标方法的信息)
     * result  目标方法的返回值
     * @annotation 注解 加载指定的目标注解
     * */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Log controllerLog, Object result) {
        handleLog(joinPoint, controllerLog, result, null);
    }

    /*
     * throwing="e" 目标方法的异常对象
     * */
    @AfterThrowing(pointcut = "@annotation(controllerLog)", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, null, e);
    }


    //处理日志的公共方法
    /*
     * joinPoint.getTarget():获取目标对象
     * joinPoint.getSignature()：获取目标方法
     * */
    private void handleLog(JoinPoint joinPoint, Log controllerLog, Object result, Exception e) {
        SysOperLog sysOperLog = new SysOperLog();
        //title  模块标题
        sysOperLog.setTitle(controllerLog.title());
        //businessType 业务类型
        sysOperLog.setBusinessType(controllerLog.businessType().name());
        //operatorType 操作类型
        sysOperLog.setOperatorType(controllerLog.operatorType().name());
        //method 方法名称
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        sysOperLog.setMethod(className + "." + methodName + "()");
        //requestMethod 请求方式
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        /*RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();*/
        String requestMethod = request.getMethod();
        sysOperLog.setRequestMethod(requestMethod);
        //operName 操作人员名字
        String token = request.getHeader("token");
        String username = JwtHelper.getUsername(token);
        sysOperLog.setOperName(username);
        //operUrl 请求url  http://www.baidu.com/admin/system
        String requestURI = request.getRequestURI();
        sysOperLog.setOperUrl(requestURI);
        //operIp ip地址
        String ipAddr = request.getLocalAddr();
        sysOperLog.setOperIp(ipAddr);
        //operParam 请求参数
        if (controllerLog.isSaveRequestData()) {
            setRequestData(joinPoint,sysOperLog);
        }
        //jsonResult 目标方法返回参数
        if (controllerLog.isSaveResponseData()) {
            //{key:value,...}
            sysOperLog.setJsonResult(JSON.toJSONString(result));
        }
        sysOperLog.setStatus(1);
        //操作状态status
        if (e != null) {
            sysOperLog.setStatus(0);
            //errorMsg
            sysOperLog.setErrorMsg(e.getMessage());
        }
        //操作时间
        sysOperLog.setOperTime(new Date());

        operLogService.saveOperLog(sysOperLog);
    }

    /**
     * 设置请求数据的方法
     *
     * @param joinPoint
     * @param sysOperLog
     */
    private void setRequestData(JoinPoint joinPoint, SysOperLog sysOperLog) {
        String requestMethod = sysOperLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            sysOperLog.setOperParam(params);
        }
    }

    /**
     * 参数拼装的方法
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!StringUtils.isEmpty(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
